<?php
/**
 * FastApp.
 * 1/3/25
 * @Author deepsea
 * @Contact (telegram:deepsea159)
 */

namespace App\Fastapp\Process\CryptoTransfer;

use App\Fastapp\Api\Service\Crypto\Config;
use App\Fastapp\Job\WalletAddressTransactionJob;
use GuzzleHttp\Client;
use Hyperf\Process\AbstractProcess;
use Hyperf\Process\Annotation\Process;

#[Process(name: 'TrxListenerProcess')]
class TrxListenerProcess extends AbstractProcess
{
    public Client $client;

    public function isEnable($server): bool
    {
        return false;
//        return env('APP_ENV') == 'prod';
    }

    public function handle(): void
    {
        // Tron (TRC-20)
        $this->client = new Client();
        $this->start();
    }

    public function formatData(): void
    {
        $response = $this->client->post(Config::TRX_URL . '/wallet/getnowblock', [
            'headers' => ['Content-Type' => 'application/json']
        ]);
        $blockData = json_decode($response->getBody()->getContents(), true);
        $tranTrx = $tranUsdt = [];
        foreach ($blockData['transactions'] ?? [] as $transaction) {
            $data = $transaction['raw_data']['contract'][0];
            if (empty($data['type'])) continue;
            if ($data['type'] == 'TransferContract') {
                $tranTrx[] = [
                    'txid' => $transaction['txID'],
                    'from' => $this->hexToBase58($data['parameter']['value']['owner_address']),
                    'to' => $this->hexToBase58($data['parameter']['value']['to_address']),
                    'value' => $data['parameter']['value']['amount'] / 1e6
                ];
            }
            if ($data['type'] == 'TriggerSmartContract' && $this->hexToBase58($data['parameter']['value']['contract_address']) === Config::TRC20_CONTRACT_ADDRESS_USDT) {
                $tr = $this->decodeTRC20Data($data['parameter']['value']['owner_address'], $data['parameter']['value']['data']);
                if (!$tr) continue;
                $tr['txid'] = $transaction['txID'];
                $tranUsdt[] = $tr;
            }
        }
        if ($tranTrx) {
            $this->handleEvent($tranTrx, 'to');//充值
            $this->handleEvent($tranTrx, 'from');//提现
        }
        if ($tranUsdt) {
            $this->handleEvent($tranUsdt, 'to', 'USDT');//充值
            $this->handleEvent($tranUsdt, 'from', 'USDT');//提现
        }
        sleep(2);
    }

    private function handleEvent(array $receivedTransactions, string $field, ?string $symbol_token = null): void
    {
        WalletAddressTransactionJob::log($receivedTransactions, 'TRX', $field, $symbol_token);
    }

    public function start(): void
    {
        $this->formatData();
        $this->start();
    }

    public function decodeTRC20Data(string $from, string $to): ?array
    {
        $methodId = substr($to, 0, 8);
        if ($methodId !== 'a9059cbb') {
            return null;
        }
        $toAddressHex = substr($to, 8, 64);
        $toAddress = '41' . substr($toAddressHex, 24);
        $toBase58 = $this->hexToBase58($toAddress);
        $valueHex = substr($to, 72, 64);
        $value = hexdec($valueHex);
        $fromBase58 = $this->hexToBase58($from);
        return [
            'from' => $fromBase58,
            'to' => $toBase58,
            'value' => $value / 1e6
        ];
    }

    public function hexToBase58($hexAddress): string
    {
        $binAddress = hex2bin($hexAddress);
        $hash1 = hash('sha256', $binAddress, true);
        $hash2 = hash('sha256', $hash1, true);
        $checksum = substr($hash2, 0, 4);
        $addressWithChecksum = $binAddress . $checksum;
        return $this->base58_encode($addressWithChecksum);
    }

    public function base58_encode($string): string
    {
        $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
        $base = strlen($alphabet);
        $num = gmp_init(bin2hex($string), 16);
        $encode = '';
        while (gmp_cmp($num, 0) > 0) {
            list($num, $rem) = gmp_div_qr($num, $base);
            $encode = $alphabet[gmp_intval($rem)] . $encode;
        }
        foreach (str_split($string) as $byte) {
            if ($byte !== "\0") {
                break;
            }
            $encode = $alphabet[0] . $encode;
        }
        return $encode;
    }
}